<html>
 <head>
  <meta charset="UTF-8">
 </head>
 <body>
  <h1 data-lake-id="VXz0P" id="VXz0P"><span data-lake-id="u49c41c6e" id="u49c41c6e">典型回答</span></h1>
  <p data-lake-id="u367548d1" id="u367548d1"><br></p>
  <p data-lake-id="ud637b9d9" id="ud637b9d9"><span data-lake-id="u69184584" id="u69184584">高效并发是从JDK 1.5 到 JDK 1.6的一个重要改进，HotSpot虚拟机开发团队在这个版本中花费了很大的精力去对Java中的锁进行优化，如适应性自旋、锁消除、锁粗化、轻量级锁和偏向锁等。这些技术都是为了在线程之间更高效的共享数据，以及解决竞争问题。</span></p>
  <p data-lake-id="u470a0935" id="u470a0935"><span data-lake-id="u86a66deb" id="u86a66deb">​</span><br></p>
  <p data-lake-id="u71b5983f" id="u71b5983f"><span data-lake-id="u81dda16e" id="u81dda16e">关于轻量级锁和偏向锁，还有适应性自旋参考：</span></p>
  <p data-lake-id="ucaf79955" id="ucaf79955"><span data-lake-id="u4fdf9704" id="u4fdf9704">​</span><br></p>
  <p data-lake-id="uae5b17b3" id="uae5b17b3"><a href="https://www.yuque.com/hollis666/vhr2ge/cv5kt1" data-lake-id="ue05ae95e" id="ue05ae95e"><span data-lake-id="u475e99dc" id="u475e99dc">https://www.yuque.com/hollis666/vhr2ge/cv5kt1</span></a></p>
  <p data-lake-id="u9d7e7c38" id="u9d7e7c38"><a href="https://www.yuque.com/hollis666/vhr2ge/dc6vfx4nfvptib2y" data-lake-id="u5c00840a" id="u5c00840a"><span data-lake-id="u98b54fa1" id="u98b54fa1">https://www.yuque.com/hollis666/vhr2ge/dc6vfx4nfvptib2y</span></a></p>
  <p data-lake-id="u768e6b2c" id="u768e6b2c"><br></p>
  <p data-lake-id="ud54e9443" id="ud54e9443"><span data-lake-id="u354d2de0" id="u354d2de0">本文，主要先来介绍一下自旋、锁消除以及锁粗化等技术。</span></p>
  <p data-lake-id="u7c7ef1a3" id="u7c7ef1a3"><br></p>
  <h3 data-lake-id="fffbc1fa" id="fffbc1fa"><span data-lake-id="u28cbd330" id="u28cbd330">自旋锁</span></h3>
  <p data-lake-id="udcf0faac" id="udcf0faac"><br></p>
  <p data-lake-id="ue9cde7e7" id="ue9cde7e7"><span data-lake-id="u80a995b5" id="u80a995b5">我们介绍的</span><code data-lake-id="u51460193" id="u51460193"><span data-lake-id="u2a510a60" id="u2a510a60">synchronized</span></code><span data-lake-id="ua6c9f88b" id="ua6c9f88b">的实现方式时，说过，它是使用</span><code data-lake-id="u0039c141" id="u0039c141"><span data-lake-id="ubf108de0" id="ubf108de0">Monitor</span></code><span data-lake-id="u3ef190be" id="u3ef190be">进行加锁，这是一种互斥锁，为了表示他对性能的影响我们称之为重量级锁。</span></p>
  <p data-lake-id="ube6f0e08" id="ube6f0e08"><br></p>
  <p data-lake-id="u4417461d" id="u4417461d"><span data-lake-id="u46567256" id="u46567256">这种互斥锁在互斥同步上对性能的影响很大，Java的线程是映射到操作系统原生线程之上的，如果要阻塞或唤醒一个线程就需要操作系统的帮忙，这就要从用户态转换到内核态，因此状态转换需要花费很多的处理器时间。</span></p>
  <p data-lake-id="ue8634bb4" id="ue8634bb4"><br></p>
  <p data-lake-id="u018d17bb" id="u018d17bb"><span data-lake-id="u3a232fb0" id="u3a232fb0">就像去银行办业务的例子，当你来到银行，发现柜台前面都有人的时候，你需要取一个号，然后再去等待区等待，一直等待被叫号。这个过程是比较浪费时间的，那么有没有什么办法改进呢？</span></p>
  <p data-lake-id="uc2960f59" id="uc2960f59"><br></p>
  <p data-lake-id="u476758c2" id="u476758c2"><span data-lake-id="u7f583eef" id="u7f583eef">有一种比较好的设计，那就是银行提供自动取款机，当你去银行取款的时候，你不需要取号，不需要去休息区等待叫号，你只需要找到一台取款机，排在其他人后面等待取款就行了。</span></p>
  <p data-lake-id="u1e349c33" id="u1e349c33"><br></p>
  <p data-lake-id="u580e1a26" id="u580e1a26"><img src="http://www.hollischuang.com/wp-content/uploads/2018/04/Pic2.png?x-oss-process=image%2Fwatermark%2Ctype_d3F5LW1pY3JvaGVp%2Csize_14%2Ctext_SmF2YSA4IEd1IFA%3D%2Ccolor_FFFFFF%2Cshadow_50%2Ct_80%2Cg_se%2Cx_10%2Cy_10"></p>
  <p data-lake-id="u2da18f55" id="u2da18f55"><br></p>
  <p data-lake-id="u7c7fe94f" id="u7c7fe94f"><span data-lake-id="u5d5b5813" id="u5d5b5813">之所以能这样做，是因为取款的这个过程相比较之下是比较节省时间的。如果所有人去银行都只取款，或者办理业务的时间都很短的话，那也就可以不需要取号，不需要去单独的休息区，不需要听叫号，也不需要再跑到对应的柜台了。</span></p>
  <p data-lake-id="ua7ca83d6" id="ua7ca83d6"><br></p>
  <p data-lake-id="u864a64b6" id="u864a64b6"><span data-lake-id="uc9659f11" id="uc9659f11">而，在程序中，Java虚拟机的开发工程师们在分析过大量数据后发现：共享数据的锁定状态一般只会持续很短的一段时间，为了这段时间去挂起和恢复线程其实并不值得。</span></p>
  <p data-lake-id="ud3f0a9cb" id="ud3f0a9cb"><br></p>
  <p data-lake-id="u5ed65cf2" id="u5ed65cf2"><span data-lake-id="u73931198" id="u73931198">如果物理机上有多个处理器，可以让多个线程同时执行的话。我们就可以让后面来的线程“稍微等一下”，但是并不放弃处理器的执行时间，看看持有锁的线程会不会很快释放锁。这个“稍微等一下”的过程就是自旋。</span></p>
  <p data-lake-id="ub8f69903" id="ub8f69903"><br></p>
  <p data-lake-id="u236b2cfb" id="u236b2cfb"><span data-lake-id="u7c19fadb" id="u7c19fadb">自旋锁在JDK 1.4中已经引入，在JDK 1.6中默认开启。</span></p>
  <p data-lake-id="u1f918132" id="u1f918132"><br></p>
  <p data-lake-id="uab393136" id="uab393136"><span data-lake-id="u553a25b2" id="u553a25b2">很多人在对于自旋锁的概念不清楚的时候可能会有以下疑问：这么听上去，自旋锁好像和阻塞锁没啥区别，反正都是等着嘛。</span></p>
  <p data-lake-id="ue6f84cb6" id="ue6f84cb6"><br></p>
  <ul list="ua199cd37">
   <li fid="u5f89635d" data-lake-id="u70590db9" id="u70590db9"><span data-lake-id="u94583874" id="u94583874"> 对于去银行取钱的你来说，站在取款机面前等待和去休息区等待叫号有一个很大的区别： </span></li>
  </ul>
  <ul list="ua199cd37" data-lake-indent="1">
   <li fid="u4e4f28d2" data-lake-id="u6fc975cc" id="u6fc975cc"><span data-lake-id="ua1d2f37e" id="ua1d2f37e"> 那就是如果你在休息区等待，这段时间你什么都不需要管，随意做自己的事情，等着被唤醒就行了。 </span></li>
   <li fid="u4e4f28d2" data-lake-id="udef3109e" id="udef3109e"><span data-lake-id="u50f64c9b" id="u50f64c9b"> 如果你在取款机面前等待，那么你需要时刻关注自己前面还有没有人，因为没人会唤醒你。 </span></li>
   <li fid="u4e4f28d2" data-lake-id="u10919858" id="u10919858"><span data-lake-id="ubb59cb9e" id="ubb59cb9e"> 很明显，这种直接去取款机前面排队取款的效率是比较高。 </span></li>
  </ul>
  <p data-lake-id="ue00f4cba" id="ue00f4cba"><br></p>
  <p data-lake-id="u30c77302" id="u30c77302"><strong><span data-lake-id="u7d462047" id="u7d462047">所以呢，自旋锁和阻塞锁最大的区别就是，到底要不要放弃处理器的执行时间。对于阻塞锁和自旋锁来说，都是要等待获得共享资源。但是阻塞锁是放弃了CPU时间，进入了等待区，等待被唤醒。而自旋锁是一直“自旋”在那里，时刻的检查共享资源是否可以被访问。</span></strong></p>
  <p data-lake-id="u6fa668b0" id="u6fa668b0"><br></p>
  <p data-lake-id="u72b538c2" id="u72b538c2"><span data-lake-id="u1667f6c1" id="u1667f6c1">由于自旋锁只是将当前线程不停地执行循环体，不进行线程状态的改变，所以响应速度更快。但当线程数不停增加时，性能下降明显，因为每个线程都需要执行，占用CPU时间。如果线程竞争不激烈，并且保持锁的时间短。适合使用自旋锁。</span></p>
  <p data-lake-id="ub0b09b51" id="ub0b09b51"><br></p>
  <h3 data-lake-id="26a4432d" id="26a4432d"><span data-lake-id="u738c45fb" id="u738c45fb">锁消除</span></h3>
  <p data-lake-id="u77ca159b" id="u77ca159b"><br></p>
  <p data-lake-id="ucd4c1d9c" id="ucd4c1d9c"><span data-lake-id="u3070a6dd" id="u3070a6dd">除了自旋锁之后，JDK中还有一种锁的优化被称之为锁消除。还拿去银行取钱的例子说。</span></p>
  <p data-lake-id="ude125d67" id="ude125d67"><br></p>
  <p data-lake-id="ufa3fecdd" id="ufa3fecdd"><span data-lake-id="u3b5f31cb" id="u3b5f31cb">你去银行取钱，所有情况下都需要取号，并且等待吗？其实是不用的，当银行办理业务的人不多的时候，可能根本不需要取号，直接走到柜台前面办理业务就好了。</span></p>
  <p data-lake-id="ue8467d7b" id="ue8467d7b"><br></p>
  <p data-lake-id="u29e5f522" id="u29e5f522"><img src="http://www.hollischuang.com/wp-content/uploads/2018/04/Pic3.png?x-oss-process=image%2Fwatermark%2Ctype_d3F5LW1pY3JvaGVp%2Csize_14%2Ctext_SmF2YSA4IEd1IFA%3D%2Ccolor_FFFFFF%2Cshadow_50%2Ct_80%2Cg_se%2Cx_10%2Cy_10"></p>
  <p data-lake-id="u2193b79e" id="u2193b79e"><br></p>
  <p data-lake-id="u750eb986" id="u750eb986"><span data-lake-id="u966080e7" id="u966080e7">能这么做的前提是，没有人和你抢着办业务。</span></p>
  <p data-lake-id="u3d609f1d" id="u3d609f1d"><br></p>
  <p data-lake-id="ua9a2ee01" id="ua9a2ee01"><span data-lake-id="u21b79b5b" id="u21b79b5b">上面的这种例子，在锁优化中被称作“锁消除”，是JIT编译器对内部锁的具体实现所做的一种优化。</span></p>
  <p data-lake-id="ub459e71a" id="ub459e71a"><br></p>
  <p data-lake-id="ue7003cdd" id="ue7003cdd"><span data-lake-id="ud39924f0" id="ud39924f0">在动态编译同步块的时候，JIT编译器可以借助一种被称为逃逸分析（Escape Analysis）的技术来判断同步块所使用的锁对象是否只能够被一个线程访问而没有被发布到其他线程。</span></p>
  <p data-lake-id="u311c8c12" id="u311c8c12"><br></p>
  <p data-lake-id="u32df22d3" id="u32df22d3"><span data-lake-id="uc648c78d" id="uc648c78d">如果同步块所使用的锁对象通过这种分析被证实只能够被一个线程访问，那么JIT编译器在编译这个同步块的时候就会取消对这部分代码的同步。</span></p>
  <p data-lake-id="u5b6fabbd" id="u5b6fabbd"><br></p>
  <p data-lake-id="u8a3bec57" id="u8a3bec57"><span data-lake-id="u399a6678" id="u399a6678">如以下代码：</span></p>
  <p data-lake-id="u24033a1d" id="u24033a1d"><br></p>
  <pre lang="java"><code>
public void f() {
    Object hollis = new Object();
    synchronized(hollis) {
        System.out.println(hollis);
    }
}
</code></pre>
  <p data-lake-id="u0728404c" id="u0728404c"><br></p>
  <p data-lake-id="u8cfb4cac" id="u8cfb4cac"><span data-lake-id="u445bf13c" id="u445bf13c">代码中对</span><code data-lake-id="u5ee0b400" id="u5ee0b400"><span data-lake-id="u36a15e27" id="u36a15e27">hollis</span></code><span data-lake-id="u723e8158" id="u723e8158">这个对象进行加锁，但是</span><code data-lake-id="u28910db7" id="u28910db7"><span data-lake-id="u28fc4bdb" id="u28fc4bdb">hollis</span></code><span data-lake-id="u48af423e" id="u48af423e">对象的生命周期只在</span><code data-lake-id="u23927d54" id="u23927d54"><span data-lake-id="uef09e25d" id="uef09e25d">f()</span></code><span data-lake-id="udb863389" id="udb863389">方法中，并不会被其他线程所访问到，所以在JIT编译阶段就会被优化掉。优化成：</span></p>
  <p data-lake-id="u8ad3742f" id="u8ad3742f"><br></p>
  <pre lang="java"><code>
public void f() {
    Object hollis = new Object();
    System.out.println(hollis);
}
</code></pre>
  <p data-lake-id="u145573ca" id="u145573ca"><br></p>
  <blockquote data-lake-id="u5dd496e2" id="u5dd496e2">
   <p data-lake-id="u6917d979" id="u6917d979"><span data-lake-id="uccdaef68" id="uccdaef68">这里，可能有读者会质疑了，代码是程序员自己写的，程序员难道没有能力判断要不要加锁吗？就像以上代码，完全没必要加锁，有经验的开发者一眼就能看的出来的。其实道理是这样，但是还是有可能有疏忽，比如我们经常在代码中使用</span><code data-lake-id="u814c092e" id="u814c092e"><span data-lake-id="ue577bbc6" id="ue577bbc6">StringBuffer</span></code><span data-lake-id="uf2706436" id="uf2706436">作为局部变量，而</span><code data-lake-id="ued1ec23e" id="ued1ec23e"><span data-lake-id="u2301be99" id="u2301be99">StringBuffer</span></code><span data-lake-id="u1a356ace" id="u1a356ace">中的</span><code data-lake-id="u60dd9aa8" id="u60dd9aa8"><span data-lake-id="u7cb255d2" id="u7cb255d2">append</span></code><span data-lake-id="u162db69e" id="u162db69e">是线程安全的，有</span><code data-lake-id="ua0c6a676" id="ua0c6a676"><span data-lake-id="u18b8ffa8" id="u18b8ffa8">synchronized</span></code><span data-lake-id="uef5bd8c2" id="uef5bd8c2">修饰的，这种情况开发者可能会忽略。这时候，JIT就可以帮忙优化，进行锁消除。</span></p>
  </blockquote>
  <p data-lake-id="ud29304d5" id="ud29304d5"><br></p>
  <p data-lake-id="u1291895b" id="u1291895b"><span data-lake-id="ucbe22797" id="ucbe22797">了解我的朋友都知道，一般到这个时候，我就会开始反编译，然后拿出反编译之后的代码来证明锁优化确实存在。</span></p>
  <p data-lake-id="ue1fb7b8f" id="ue1fb7b8f"><br></p>
  <p data-lake-id="u4693b110" id="u4693b110"><span data-lake-id="u7f29140b" id="u7f29140b">但是，之前很多例子之所以可以用反编译工具，是因为那些“优化”，如语法糖等，是在</span><code data-lake-id="uaa92d155" id="uaa92d155"><span data-lake-id="ufcad6b9c" id="ufcad6b9c">javac编译</span></code><span data-lake-id="ucfdfd55e" id="ucfdfd55e">阶段发生的，并不是在</span><code data-lake-id="u2fa37e85" id="u2fa37e85"><span data-lake-id="ua4d1ce66" id="ua4d1ce66">JIT编译</span></code><span data-lake-id="ufb3729d5" id="ufb3729d5">阶段发生的。而锁优化，是JIT编译器的功能，所以，无法使用现有的反编译工具查看具体的优化结果。（关于javac编译和JIT编译的关系和区别，我在我的知识星球中单独发了一篇文章介绍。）</span></p>
  <p data-lake-id="u34ca75f8" id="u34ca75f8"><br></p>
  <blockquote data-lake-id="ud22c86bb" id="ud22c86bb">
   <p data-lake-id="u5481c958" id="u5481c958"><span data-lake-id="ud2513827" id="ud2513827">但是，如果读者感兴趣，还是可以看的，只是会复杂一点，首先你要自己build一个fasttest版本的jdk，然后在使用java命令对</span><code data-lake-id="u617b28d2" id="u617b28d2"><span data-lake-id="ub61f2ab2" id="ub61f2ab2">.class</span></code><span data-lake-id="u69bf497c" id="u69bf497c">文件进行执行的时候加上</span><code data-lake-id="u8d0bfd35" id="u8d0bfd35"><span data-lake-id="u7a1d720d" id="u7a1d720d">-XX:+PrintEliminateLocks</span></code><span data-lake-id="u687a4ee7" id="u687a4ee7">参数。而且jdk的模式还必须是server模式。</span></p>
  </blockquote>
  <p data-lake-id="u96d4f09f" id="u96d4f09f"><br></p>
  <p data-lake-id="u673b90d2" id="u673b90d2"><span data-lake-id="u23d57155" id="u23d57155">总之，读者只需要知道，在使用</span><code data-lake-id="u24c8e0ad" id="u24c8e0ad"><span data-lake-id="u1860d154" id="u1860d154">synchronized</span></code><span data-lake-id="u8d8bbf7f" id="u8d8bbf7f">的时候，如果JIT经过逃逸分析之后发现并无线程安全问题的话，就会做锁消除。</span></p>
  <p data-lake-id="u0228d7e7" id="u0228d7e7"><br></p>
  <h3 data-lake-id="6802d66d" id="6802d66d"><span data-lake-id="ucf39ec4d" id="ucf39ec4d">锁粗化</span></h3>
  <p data-lake-id="uce7deb6f" id="uce7deb6f"><br></p>
  <p data-lake-id="u345f9203" id="u345f9203"><span data-lake-id="u8bef35a6" id="u8bef35a6">很多人都知道，在代码中，需要加锁的时候，我们提倡尽量减小锁的粒度，这样可以避免不必要的阻塞。</span></p>
  <p data-lake-id="u878c3972" id="u878c3972"><br></p>
  <p data-lake-id="u2501102c" id="u2501102c"><span data-lake-id="ufc078293" id="ufc078293">这也是很多人愿意使用同步代码块来代替同步方法的原因，因为往往他的粒度会更小一些，这其实是很有道理的。</span></p>
  <p data-lake-id="u17290fbb" id="u17290fbb"><br></p>
  <p data-lake-id="udb88b185" id="udb88b185"><span data-lake-id="u5cb92341" id="u5cb92341">还是我们去银行柜台办业务，最高效的方式是你坐在柜台前面的时候，只办和银行相关的事情。如果这个时候，你拿出手机，接打几个电话，问朋友要往哪个账户里面打钱，这就很浪费时间了。最好的做法肯定是提前准备好相关资料，在办理业务时直接办理就好了。</span></p>
  <p data-lake-id="uaa74f1dc" id="uaa74f1dc"><br></p>
  <p data-lake-id="uaabda770" id="uaabda770"><img src="http://www.hollischuang.com/wp-content/uploads/2018/04/Pic4.png?x-oss-process=image%2Fwatermark%2Ctype_d3F5LW1pY3JvaGVp%2Csize_11%2Ctext_SmF2YSA4IEd1IFA%3D%2Ccolor_FFFFFF%2Cshadow_50%2Ct_80%2Cg_se%2Cx_10%2Cy_10"></p>
  <p data-lake-id="u56184d5d" id="u56184d5d"><br></p>
  <p data-lake-id="u4aeabba5" id="u4aeabba5"><span data-lake-id="u4a958598" id="u4a958598">加锁也一样，把无关的准备工作放到锁外面，锁内部只处理和并发相关的内容。这样有助于提高效率。</span></p>
  <p data-lake-id="u5a23c9ef" id="u5a23c9ef"><br></p>
  <p data-lake-id="uacc025b3" id="uacc025b3"><span data-lake-id="u4c8bfcd6" id="u4c8bfcd6">那么，这和锁粗化有什么关系呢？可以说，大部分情况下，减小锁的粒度是很正确的做法，只有一种特殊的情况下，会发生一种叫做锁粗化的优化。</span></p>
  <p data-lake-id="u51bb3b7f" id="u51bb3b7f"><br></p>
  <p data-lake-id="u51a2a415" id="u51a2a415"><span data-lake-id="u898594ef" id="u898594ef">就像你去银行办业务，你为了减少每次办理业务的时间，你把要办的五个业务分成五次去办理，这反而适得其反了。因为这平白的增加了很多你重新取号、排队、被唤醒的时间。</span></p>
  <p data-lake-id="uf9cb1f8d" id="uf9cb1f8d"><br></p>
  <p data-lake-id="ubca49940" id="ubca49940"><span data-lake-id="ue0f5d611" id="ue0f5d611">如果在一段代码中连续的对同一个对象反复加锁解锁，其实是相对耗费资源的，这种情况可以适当放宽加锁的范围，减少性能消耗。</span></p>
  <p data-lake-id="u7261b48d" id="u7261b48d"><br></p>
  <p data-lake-id="u3d92f44d" id="u3d92f44d"><span data-lake-id="u66e82705" id="u66e82705">当JIT发现一系列连续的操作都对同一个对象反复加锁和解锁，甚至加锁操作出现在循环体中的时候，会将加锁同步的范围扩散（粗化）到整个操作序列的外部。</span></p>
  <p data-lake-id="u8ae21604" id="u8ae21604"><br></p>
  <p data-lake-id="u85e082b6" id="u85e082b6"><span data-lake-id="u39f7ea9a" id="u39f7ea9a">如以下代码：</span></p>
  <p data-lake-id="u8e49e207" id="u8e49e207"><br></p>
  <pre lang="java"><code>
for(int i=0;i&lt;100000;i++){  
    synchronized(this){  
        do();  
}
</code></pre>
  <p data-lake-id="u93ff2529" id="u93ff2529"><br></p>
  <p data-lake-id="u054386a8" id="u054386a8"><span data-lake-id="u4ed6c4cd" id="u4ed6c4cd">会被粗化成：</span></p>
  <p data-lake-id="u70d206eb" id="u70d206eb"><br></p>
  <pre lang="java"><code>
synchronized(this){  
    for(int i=0;i&lt;100000;i++){  
        do();  
}
</code></pre>
  <p data-lake-id="u1227524c" id="u1227524c"><br></p>
  <p data-lake-id="ud5620fe9" id="ud5620fe9"><strong><span data-lake-id="u98399a89" id="u98399a89">这其实和我们要求的减小锁粒度并不冲突。减小锁粒度强调的是不要在银行柜台前做准备工作以及和办理业务无关的事情。而锁粗化建议的是，同一个人，要办理多个业务的时候，可以在同一个窗口一次性办完，而不是多次取号多次办理。</span></strong></p>
  <p data-lake-id="ub036dfae" id="ub036dfae"><br></p>
  <h3 data-lake-id="25f9c7fa" id="25f9c7fa"><span data-lake-id="u382210a5" id="u382210a5">总结</span></h3>
  <p data-lake-id="u8176d85f" id="u8176d85f"><br></p>
  <p data-lake-id="u75ed9e3b" id="u75ed9e3b"><span data-lake-id="u3cd047df" id="u3cd047df">自Java 6/Java 7开始，Java虚拟机对内部锁的实现进行了一些优化。这些优化主要包括锁消除（Lock Elision）、锁粗化（Lock Coarsening）、偏向锁（Biased Locking）以及适应性自旋锁（Adaptive Locking）。这些优化仅在Java虚拟机server模式下起作用（即运行Java程序时我们可能需要在命令行中指定Java虚拟机参数“-server”以开启这些优化）。</span></p>
  <p data-lake-id="ueae86272" id="ueae86272"><br></p>
  <p data-lake-id="uc272ae36" id="uc272ae36"><span data-lake-id="u51c37175" id="u51c37175">本文主要介绍了自旋锁、锁粗化和锁消除的概念。在JIT编译过程中，虚拟机会根据情况使用这三种技术对锁进行优化，目的是减少锁的竞争，提升性能。</span></p>
 </body>
</html>